package auditoryUI;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Arc2D;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;

/**
 * the UI that display touch pad on screen.
 * 
 * @author Stephen
 * 
 */
public class SimpleUI extends JPanel {

	public static final double DEBUG_ZOOM = 0.5;
	public static final int DEBUG_WIDTH = 400; // 1280;
	public static final int DEBUG_HEIGHT = 700; // 1024;
	public static final int DEBUG_RADIUS = DEBUG_WIDTH / 2 - 20;
	static int miX, miY, miLastX, miLastY;
//	public static double mdStartAngle = Math.toDegrees(Math.PI / 12.0 + Math.PI
//			/ 2.0);
	public static double mdStartAngle = 0.0;
	private int miCurrentMenuBreadth = 12;
	int[] hysteresisOffsets;
	// The following allows dynamic assignation of offsets to angles and the
	// inner
	// circle radius, to support hysteresis.
	// Value of index 0 is the current amount to add to the radius of the inner
	// circle.
	// Following values are degrees to add to pie borders, starting from the
	// right vertex of
	// the zone 0.
	static final int RADIUS_HYSTERESIS = 3;
	static final int ANGULAR_HYSTERESIS = 3;

	private static final double OUTER_ZONE_THRESHOLD = DEBUG_RADIUS - 110; // toronto
	
	private String[] mode = {"Button", "Gasture", "Tilt"};

	private JLabel itemName;

	private JFrame mainFrame;
	
	private JComboBox methods;
	
	private JWindow msg, goal;
	
	private float[] weights = null;
	

	public SimpleUI(final SimpleDriver sd, int num) {
		mainFrame = new JFrame("Touch Pad");
		itemName = new JLabel();
		methods = new JComboBox(mode);
		clearHysteresis();
		JPanel touchPadPane = (JPanel) mainFrame.getContentPane();
		touchPadPane.add(methods, BorderLayout.NORTH);
		touchPadPane.add(itemName, BorderLayout.SOUTH);
		touchPadPane.add(this, BorderLayout.CENTER);
		mainFrame.setSize(250,400);//original value is 220
		mainFrame.setVisible(true);
		mainFrame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent evt) {
				sd.onExit();
			}
		});
		miCurrentMenuBreadth = num;
		msg = new JWindow(mainFrame);
		goal = new JWindow(mainFrame);
	}
	
	public void setMenuBreadth(int num){
		miCurrentMenuBreadth = num;
	}
	
	public void setWeights(float[] w){
		weights = null;
	}
	
	public String getCurrrentInputMode(){
		return (String) methods.getSelectedItem();
	}

	public void paintComponent(Graphics gg) {
		mdStartAngle = 90.0-(360.0/(double)miCurrentMenuBreadth)/2;

		Graphics2D g = (Graphics2D) gg;
		g.scale(DEBUG_ZOOM, DEBUG_ZOOM);
		
		g.clearRect(0, 0, DEBUG_WIDTH, DEBUG_HEIGHT);

		g.setColor(Color.BLUE);
		float strokeThickness = 2.0f;
		BasicStroke stroke = new BasicStroke(strokeThickness);
		g.setStroke(stroke);

		drawArcs( miCurrentMenuBreadth, g);

		Point p1 = this.touchPadCornerToScreen(new Point(miX, miY));
		Point p2 = this.touchPadCornerToScreen(new Point(miLastX, miLastY));
		strokeThickness = 10.0f;
		stroke = new BasicStroke(strokeThickness);
		g.setStroke(stroke);
		gg.drawLine((int) p1.getX(), (int) p1.getY(), (int) p2.getX(), (int) p2
				.getY());
	}
	
	
	public void showMessage(String content){
		if (msg.getContentPane().getComponentCount() != 0){
			msg.getContentPane().removeAll();
		}
		Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
		JLabel tmp = new JLabel(content);
		tmp.setFont(new Font("Helvetica", Font.BOLD, 30));
		msg.getContentPane().add(tmp);
		msg.setSize(content.length()*20,100);
		msg.setLocation((screenSize.width-msg.getWidth())/2, 
				(screenSize.height-msg.getHeight())/3 + (int)(msg.getHeight()*1.2*1));
		msg.setVisible(true);
		
	}
	
	public void showGoal(String content){
		if (goal.getContentPane().getComponentCount() != 0){
			goal.getContentPane().removeAll();
		}
		Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
		JLabel tmp = new JLabel(content);
		tmp.setFont(new Font("Helvetica", Font.BOLD, 30));
		goal.getContentPane().add(tmp);
		goal.setSize(content.length()*20,100);
		goal.setLocation((screenSize.width-msg.getWidth())/2, 
				(screenSize.height-msg.getHeight())/3 + (int)(msg.getHeight()*1.2*1)- 100);
		goal.setVisible(true);
		
	}
	
	public void hideGoal(){
		goal.setVisible(false);
	}

	/**
	 * Takes touch pad points where origin is at the top left corner, and
	 * translate them into screen drawing points.
	 * 
	 * @param touchPadPoint
	 * @return
	 */
	private Point touchPadCornerToScreen(Point touchPadRawPoint) {
		Point screenPoint = new Point();
		// the translation and scaling are
		double x = touchPadRawPoint.getX() * (DEBUG_RADIUS * 2)
				/ (Constants.TP_VMAX - Constants.TP_VMIN) + (DEBUG_WIDTH - 2 * DEBUG_RADIUS) / 2; 										// 1150
		double y = touchPadRawPoint.getY() * (DEBUG_RADIUS * 2)
				/ (Constants.TP_HMAX - Constants.TP_HMIN) + (DEBUG_HEIGHT - 2 * DEBUG_RADIUS) / 2; 							// 1040
		screenPoint.setLocation(x, y);
		return screenPoint;
	}

	/**
	 * Draw the arcs of the circle, and try to see them in action in debugging
	 * 
	 * @param startingAngle
	 *            in degrees now
	 * @param nElements
	 * @param weights
	 * @param gg
	 */
	private void drawArcs(int nElements, Graphics2D gg) {
		gg.setColor(Color.black);

		// 4.5 calculate the delta sum
		float totalSum = 0F;

		if (weights == null)
			totalSum = nElements; // if no weight, the total sum is normal =
									// 360 degrees
		else {
			// we calculate the total weight
			totalSum = (float) getSumOfIntArray(weights, weights.length);
		}// end of else
		int extension = 20; // the maximum you can go is 20, so we set it to 20
		int extendedRadius = DEBUG_RADIUS + extension;

		float arcX = (float) ((DEBUG_WIDTH - 2 * extendedRadius) / 2);
		float arcY = (float) ((DEBUG_HEIGHT - 2 * extendedRadius) / 2);
		float angleStart = (float) mdStartAngle;
		Arc2D arc = new Arc2D.Float(arcX, arcY, (float) 2 * extendedRadius,
				(float) 2 * extendedRadius, 0, 0, Arc2D.PIE);
		
		float cumulativeAngles = 0.0f;

		for (int i = 0; i < nElements; i++) {
			// 5. we need to check to see if which zone the point is located at
			// float elementWeight = 360F/nElements; //if the weights is null,
			// we set element weight to 1
			float elementWeight = 1.0F;
			if (weights != null)elementWeight = weights[i];
			float angleExtent = (elementWeight / totalSum) * 360;
			arc.setAngleStart((float) (angleStart + cumulativeAngles));
			arc.setAngleExtent(angleExtent);
			cumulativeAngles += angleExtent; // we set
															
			gg.setColor(new Color(i % 3 / 2f, 0, 1 - i % 2 / 1f));

			gg.draw(arc);
		}

		// Draw inner circle
		// -- Take radius offset into account (see hysteresisOffsets[]
		// declaration)
		drawCircle(gg, OUTER_ZONE_THRESHOLD + hysteresisOffsets[0]);
	}

	/**
	 * if only one up value is taken, use 0 for start index
	 * 
	 * @param array
	 * @param upTo
	 * @return
	 */
	public float getSumOfIntArray(float[] array, int upTo) {
		float total = 0;
		for (int i =0; i< upTo; i++){
			total += array[i];
		}
		return total;
	}

	/**
	 * utility method for getting the sum of an int array upto the upto index.
	 * For example for an array of 10 numbers, if upto is 9, then it will only
	 * get the sum of the first 9 numbers
	 * 
	 * N 0 1 . 2 5 4 3
	 * 
	 * we assuming this array self connect from the head to toe The starting
	 * point can be any number, and the end of point can be any number as well.
	 * 
	 * @param array
	 * @return
	 */
	public int getSumOfIntArray(int[] array, int startIndex, int upTo) {
		int sum = 0;

		if (array == null)
			return sum;
		else {
			// there are several situations
			// we assuming this array is around a circle
			// 1. if startIndex <= upTo
			if (upTo > array.length)
				upTo = array.length;
			if (upTo < 0)
				upTo = 0;

			if (startIndex > array.length)
				startIndex = array.length;
			if (startIndex < 0)
				startIndex = 0;

			if (startIndex <= upTo) {
				for (int i = startIndex; i < upTo; i++) {
					sum += array[i];
				}// end of for loop
			} else if (startIndex > upTo) {
				for (int i = startIndex; i < array.length; i++) {
					sum += array[i];
				}// end of for loop

				for (int i = 0; i < upTo; i++) {
					sum += array[i];
				}// end of for loop
			}

			return sum;
		}// end of else
	}// end of method

	private void drawCircle(Graphics2D g, double radius) {
		float arcX = (float) ((DEBUG_WIDTH - 2 * radius) / 2), arcY = (float) (DEBUG_HEIGHT - 2 * radius) / 2;
		g.setColor(Color.white);
		g.fillOval((int) (arcX), (int) (arcY), (int) (2 * radius),
				(int) (2 * radius));
		g.setColor(Color.black);
		g.drawOval((int) (arcX), (int) (arcY), (int) (2 * radius),
				(int) (2 * radius));
	}
	
	private void clearHysteresis() {
		// get the number of zones 
		int numItems = miCurrentMenuBreadth;
		hysteresisOffsets = new int[numItems+1];
	}
	
	
	public void updateUI(int x, int y, int index, int item, int l){
		miLastX = x;
		miLastY = y;
		miX = x;
		miY = y;
		mainFrame.setTitle(miX + "  " + miY + "  item " + index + " at level " + l);
		miCurrentMenuBreadth = item;
		repaint();
	}
	
	

}